#include "aerPoolAllocator.hpp"


namespace aer {
  
PoolAllocator::PoolAllocator()
    : m_data(NULL),
      m_pFreeList(NULL),
      m_alignmentPadding(0u),
      m_capacity(0u),
      m_size(0u),
      m_chunkSize(0u)
{
}

PoolAllocator::~PoolAllocator()
{
  if (!m_data) {
    return;
  }
  
  release();
}


void PoolAllocator::initialize(const U32 numChunk, const U32 chunkSize)
{
  AER_ASSERT( m_data == NULL );
  AER_ASSERT( chunkSize >= sizeof(UPTR) );//~   to store free chunk list pointer  
  // AER_ASSERT( isPowerOfTwo( chunkSize ) );//~  for base alignment
  
  
  m_capacity = numChunk;
  m_chunkSize = chunkSize;
  
  // Add +1 to capacity for base memory alignment
  void *p = calloc( m_capacity+1u, m_chunkSize);
  
  // Align base memory on chunkSize
  UPTR rawAddress = UPTR(p);
  UPTR mask = (m_chunkSize - 1u);
  UPTR misalignment = (rawAddress & mask);
  m_alignmentPadding = m_chunkSize - misalignment;

  m_data = (void*)(UPTR(p) + m_alignmentPadding);
  
  setupFreeChunkList();
}

void PoolAllocator::setupFreeChunkList()
{
  // Set the list of free chunks
  UPTR *pNextAddress = (UPTR*)(m_data);
  
  for (U32 i=0u; i<m_capacity-1u; ++i)
  {
    *pNextAddress = UPTR(pNextAddress) + m_chunkSize; // [alt: put an offset]
    pNextAddress = (UPTR*)(*pNextAddress);
  }
  *pNextAddress = 0;
  
  // Head of the list
  m_pFreeList = m_data;
}


void PoolAllocator::release()
{
  AER_ASSERT( m_data != NULL );
  
  m_pFreeList = NULL;  
  void *p = (void*)(UPTR(m_data) - m_alignmentPadding);
  free( p );
}




void* PoolAllocator::allocate()
{
  AER_ASSERT( m_capacity != 0u );
  
  if (m_size >= m_capacity) {
    return NULL;
  }
  ++m_size;
  
  // Get the first free cell
  UPTR* address = (UPTR*)(m_pFreeList);
  UPTR next = *address;
  
  // Update the free chunk list ( Head = Head->next )
  m_pFreeList = (void*)(next);//
    
  return address;
}

void PoolAllocator::deallocate(void *p)
{
  AER_ASSERT( m_size > 0 );
  --m_size;
  
  if (m_pFreeList != NULL) {
    *((UPTR*)p) = *((UPTR*)m_pFreeList);
  } else  {
    *((UPTR*)p) = 0;
  }
  
    
  // p becomes the new list head
  m_pFreeList = p;
}

  
} // aer
